﻿using System;
using System.Collections.Generic;
using System.Linq;
using BOL.Linq;
using BOL.Maths.Distributions;

namespace BOL.Algorithms.Classification
{
    /// <summary>
    /// Implements a generic LDA classifier.
    /// </summary>
    /// <see>http://en.wikipedia.org/wiki/Linear_discriminant_analysis</see>
    public class FishersLinearDiscriminant<TClass> : ClassifierBase<double, TClass>
    {
        #region Public properties

        public IMultivariateDistribution<double> Distribution;
        public IDictionary<TClass, IMultivariateDistribution<double>> ClassDistribution { get; private set; }

        #endregion

        #region Constructors

        public FishersLinearDiscriminant(IMultivariateDistribution<double> distribution, IDictionary<TClass, IMultivariateDistribution<double>> classDistribution)
        {
            if (distribution == null)
                throw new ArgumentNullException("distribution");
            if (classDistribution == null)
                throw new ArgumentNullException("classDistribution");

            Distribution = distribution;
            ClassDistribution = classDistribution;
        }

        #endregion

        #region Public methods

        public void Train<TSource>(IEnumerable<TSource> source, Func<TSource, TClass> classSelector, Func<TSource, double[]> featureSelector)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (classSelector == null)
                throw new ArgumentNullException("classSelector");
            if (featureSelector == null)
                throw new ArgumentNullException("featureSelector");

            var list = source.ToList();
            Distribution.MaximumLikelihoodEstimate(list.Select(featureSelector));
            var classList = list.GroupBy(classSelector).ToDictionary(x => x.Key, x => x.Select(featureSelector));
            ClassDistribution.ForEach(x => x.Value.MaximumLikelihoodEstimate(classList[x.Key]));
        }

        public void Train<TSource>(IEnumerable<TSource> source, Func<TSource, TClass> classSelector, Func<TSource, double[]> featuresSelector, int numberOfIterations)
        {
            throw new NotImplementedException();
        }

        public override TClass Classify(double[] features)
        {
            //if (features == null)
            //    throw new ArgumentNullException("features");
            //if (features.Length != Distribution.K)
            //    throw new ArgumentOutOfRangeException("features");

            //var numberOfFeatures = Distribution.K;
            //var numberOfClasses = ClassDistribution.Count;

            //var between = new double[numberOfFeatures][];
            //var muI = ClassDistribution.Select(x => x.Value.Mean.ToArray()).ToList();
            //var mu = Distribution.Mean.ToArray();
            
            //for (var i = 0; i < numberOfFeatures; i++)
            //    between[i] = muI.Select(x => x.Select(y => Math.Pow(y - mu[i], 2)).Sum()/numberOfClasses).ToArray();

            //var within = Distribution.Covariance;

            throw new NotImplementedException();
        }

        #endregion
    }
}
